/**
 * Copyright 2008 Mathias Doenitz, http://lis.to/
 *
 * This file is part of the lis.to java desktop client. The lis.to java desktop client is free software: you can
 * redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * The lis.to java desktop client is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with the lis.to java desktop client.
 * If not, see http://www.gnu.org/licenses/
 */

package listo.client.tasklists;

import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import listo.client.Formatter;
import listo.client.ImageProvider;
import listo.client.Lang;
import listo.client.Preferences;
import listo.client.model.Task;
import listo.utils.swing.HyperlinkLabel;
import listo.utils.swing.HyperlinkLabelUI;
import listo.utils.swing.SwingUtils;
import listo.utils.types.DateTime;
import org.apache.commons.lang.StringUtils;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.text.DateFormat;

@Singleton
public class TaskListRenderer extends JPanel implements ListCellRenderer {
    
    private static final Border NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);

    private boolean initialized;

    private Color colorCellBackground;
    private Color colorCellForeground;
    private Color colorFocusedCellBackground;
    private Color colorFocusedCellForeground;
    private Color colorSelectedCellBackground;
    private Color colorSelectedCellForeground;

    private final JCheckBox checkBox;
    private final HyperlinkLabel descLabel;
    private final JLabel noteLabel;
    private final JLabel reminderLabel;
    private final JLabel dueLabel;

    private final DateFormat timeFormat;
    private final Provider<Formatter> formatterProvider;
    private final Preferences preferences;

    @Inject
    public TaskListRenderer(ImageProvider imageProvider, Lang lang, Provider<Formatter> formatterProvider,
                            Preferences preferences) {
        this.formatterProvider = formatterProvider;
        this.preferences = preferences;
        setLayout(new BorderLayout(5, 5));

        timeFormat = lang.getTimeFormat();

        JPanel centerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
        centerPanel.setOpaque(false);

        checkBox = new JCheckBox();
        checkBox.setOpaque(false);
        centerPanel.add(checkBox);

        descLabel = new HyperlinkLabel();
        descLabel.setForceUnderline(true);
        centerPanel.add(descLabel);

        noteLabel = new JLabel(imageProvider.getIcon("16x16/note.png"));
        centerPanel.add(noteLabel);

        reminderLabel = new JLabel(imageProvider.getIcon("16x16/alarm.png"));
        reminderLabel.setFont(UIManager.getFont("Label.font").deriveFont(9f));
        centerPanel.add(reminderLabel);

        add(centerPanel, BorderLayout.CENTER);

        JPanel eastPanel = new JPanel();
        eastPanel.setOpaque(false);
        eastPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 3));
        dueLabel = new JLabel();
        dueLabel.setFont(UIManager.getFont("Label.font").deriveFont(Font.BOLD));
        eastPanel.add(dueLabel);
        add(eastPanel, BorderLayout.EAST);
    }

    private void initialize(JList list) {
        setOpaque(true);

        colorCellBackground = list.getBackground();
        colorCellForeground = list.getForeground();
        colorFocusedCellBackground = list.getSelectionBackground();
        colorFocusedCellForeground = list.getSelectionForeground();
        colorSelectedCellBackground = SwingUtils.setAlpha(colorFocusedCellBackground, 128);
        colorSelectedCellForeground = colorFocusedCellForeground;

        initialized = true;
    }

    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
                                                  boolean cellHasFocus) {
        if (!initialized) initialize(list);

        if (cellHasFocus) {
            setBackground(colorFocusedCellBackground);
            setForeground(colorFocusedCellForeground);
        } else if (isSelected) {
            setBackground(colorSelectedCellBackground);
            setForeground(colorSelectedCellForeground);
        } else {
            setBackground(colorCellBackground);
            setForeground(colorCellForeground);
        }

        Border border = null;
        if (cellHasFocus) {
            if (isSelected) border = UIManager.getBorder("List.focusSelectedCellHighlightBorder");
            if (border == null) border = UIManager.getBorder("List.focusCellHighlightBorder");
        } else {
            border = NO_FOCUS_BORDER;
        }
        setBorder(border);

        Formatter formatter = formatterProvider.get();

        Task task = (Task) value;
        String desc = task.getDesc();
        DateTime due = task.getDue();
        String reminder = formatter.toLongReminderString(due, task.getReminder());
        boolean hasReminder = StringUtils.isNotEmpty(reminder);
        boolean hasNote = StringUtils.isNotEmpty(task.getNote());
        boolean isCompleted = task.isCompleted();
        boolean isDeleted = !task.hasFolders();
        boolean isOverdue = due != null && new DateTime().after(due);

        // **************** render ******************

        checkBox.setSelected(isCompleted);

        descLabel.setStrikeThrough(isDeleted && (list instanceof PopupTaskList) ||
                isCompleted && preferences.strikeOutCompletedTasks());
        descLabel.setText(desc);
        descLabel.setForeground(isOverdue && list instanceof MainFormTaskList ? Color.RED : getForeground());
        descLabel.setLinkColor(cellHasFocus ? Color.YELLOW : HyperlinkLabel.DEFAULT_LINK_COLOR);

        noteLabel.setVisible(hasNote);

        reminderLabel.setVisible(hasReminder);
        if (hasReminder) {
            reminderLabel.setText(reminder);
            reminderLabel.setForeground(getForeground());
        }

        dueLabel.setVisible(due != null);
        if (due != null) {
            String text = formatter.toLongDateString(due);
            if (task.hasDueTime()) {
                text += ", " + due.format(timeFormat);
            }
            dueLabel.setText(text);
            dueLabel.setForeground(getForeground());
        }

        return this;
    }

    public boolean isInCheckBox(Point point) {
        int rowHeight = getPreferredSize().height;
        int rowTop = point.y - (point.y % rowHeight);
        Point checkBoxLoc = checkBox.getLocation();
        int checkBoxEdgeLength = checkBox.getHeight();
        Rectangle checkBoxRect = new Rectangle(checkBoxLoc.x, rowTop + checkBoxLoc.y, checkBoxEdgeLength,
                checkBoxEdgeLength);
        return checkBoxRect.contains(point);
    }

    public boolean isInNoteIcon(Task task, Point point) {
        if (task == null || StringUtils.isEmpty(task.getNote())) return false;

        getListCellRendererComponent(null, task, 0, false, false);
        int rowHeight = getPreferredSize().height;
        int rowTop = point.y - (point.y % rowHeight);
        Rectangle noteLabelBounds = noteLabel.getBounds();
        noteLabelBounds.setLocation(noteLabelBounds.x, noteLabelBounds.y + rowTop);
        return noteLabelBounds.contains(point);
    }

    public String isInUrl(Task task, Point point) {
        if (task == null) return null;

        getListCellRendererComponent(null, task, 0, false, false);
        int rowHeight = getPreferredSize().height;
        ((HyperlinkLabelUI) descLabel.getUI()).doLayout(descLabel, 0, 0);
        Point loc = descLabel.getLocation();
        int ix = descLabel.isOverLink(new Point(point.x - loc.x, loc.y - point.y % rowHeight));
        return ix == -1 ? null : descLabel.getUrlMatches()[ix].url;
    }

    // workaround for drag & drop cursor flickering problem: overriding this method prevents cursor flicker
    public boolean isVisible() {
        return false;
    }

    /**
     * ************************************************
     * overridden for performance reasons
     * *************************************************
     */

    /*public void validate()
    {
    }

    public void invalidate()
    {
    }

    public void revalidate()
    {
    }*/
    public void repaint() {
    }

    public void repaint(Rectangle r) {
    }

    public void repaint(long tm, int x, int y, int width, int height) {
    }

    protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
    }

    public void firePropertyChange(String propertyName, byte oldValue, byte newValue) {
    }

    public void firePropertyChange(String propertyName, char oldValue, char newValue) {
    }

    public void firePropertyChange(String propertyName, short oldValue, short newValue) {
    }

    public void firePropertyChange(String propertyName, int oldValue, int newValue) {
    }

    public void firePropertyChange(String propertyName, long oldValue, long newValue) {
    }

    public void firePropertyChange(String propertyName, float oldValue, float newValue) {
    }

    public void firePropertyChange(String propertyName, double oldValue, double newValue) {
    }

    public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
    }
}
